package com.sneakxy.cloudbase.platform.repository;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.TimeUnit;

import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.eis.AbstractSessionDAO;
import org.springframework.data.redis.core.RedisTemplate;

public class ShiroSessionRepository extends AbstractSessionDAO {
	
	public static final String SHIRO_SESSION_REPOSITORY_NAME = "CloudBaseShiroSessionRepository";
	
	public static final String SHIRO_SESSION_KEY_PREFIX = "SHIRO_KEY_";

	private RedisTemplate<String, Object> redisTemplate;
	
	public ShiroSessionRepository(RedisTemplate<String, Object> redisTemplate) {
		this.redisTemplate = redisTemplate;
	}
	
	public String getShiroSessionRepositoryName() {
		return SHIRO_SESSION_REPOSITORY_NAME;
	}
	
	@Override
	public void update(Session session) throws UnknownSessionException {
		redisTemplate.opsForValue().set(sessionIdKey(session.getId()), session, session.getTimeout(), TimeUnit.MILLISECONDS);
	}

	@Override
	public void delete(Session session) {
		redisTemplate.delete(sessionIdKey(session.getId()));
	}

	@Override
	public Collection<Session> getActiveSessions() {
		List<Session> result = new ArrayList<>();
		Iterator<String> keys = this.redisTemplate.keys(StringUtils.join(SHIRO_SESSION_KEY_PREFIX, "*")).iterator();
		while(keys.hasNext()) {
			result.add((Session) redisTemplate.opsForValue().get(keys.next()));
		}
		return result;
	}
	
	public long getActiveSessionCount() {
		long count = 0;
		Iterator<String> keys = this.redisTemplate.keys(StringUtils.join(SHIRO_SESSION_KEY_PREFIX, "*")).iterator();
		while(keys.hasNext()) {
			count ++;
		}
		return count;
	}

	@Override
	protected Serializable doCreate(Session session) {
		Serializable sessionId = this.generateSessionId(session);
	    this.assignSessionId(session, sessionId);
	    if (sessionId == null) {
            throw new NullPointerException("sessionId cannot be null.");
        }
	    redisTemplate.opsForValue().setIfAbsent(sessionIdKey(sessionId), session);
		return sessionId;
	}

	@Override
	protected Session doReadSession(Serializable sessionId) {
		return (Session) redisTemplate.opsForValue().get(sessionIdKey(sessionId));
	}

	protected String sessionIdKey(Serializable sessionId) {
		return StringUtils.join(SHIRO_SESSION_KEY_PREFIX, sessionId);
	}

}
